﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PMisuseAn.behaviour.model;
using PMisuseAn.util;
using System.Collections.Specialized;
using System.Collections;
using PMisuseAn.io;

namespace PMisuseAn.behaviour.adaptation
{
    public class AdaptationStratagy
    {
        //static int noDefinedThreatOccurance;
        //static int totalNoThreatOccurance;
        //static int noDefinedContextOccurance;
        //static int totalNoContextOccurance;
        static PolicyStatementAnalyser statAnalyser = new PolicyStatementAnalyser();
        static Stack<int> interactionSeqNos = HistorySerialisationEngine.getStackedInteractionList();

        public static List<Dictionary<List<Threat>, string>> threatMitigationActions;
        //public static Dictionary<Threat, string> threatMitigationActions = new Dictionary<Threat, string>();

        public static List<Attribute> threathenedOperationalAttributes;
        public static string adaptationAction;
        public static List<double> maxUtilityFunctions;
        public static List<double> sensitivity_level_utilitys;
        public static List<double> obf_level_utilitys;
        public static double sensitivity_level_constant;
        public static double obf_level_constant;
        public static List<string> decisions;

        public static string adaptationRule;
        public static string metaAdaptationRule;
        public static List<double> threatFrequencys;
        public static List<List<string>> h_adaptationActionss;

        public static string getAdaptationStratagy(List<Threat> threats)
        {
            if (threatMitigationActions == null)
            {
                threatMitigationActions = new List<Dictionary<List<Threat>, string>>();
            }

            //setNoThreatOccurances(threat); //provides frequency of threat occurance

            //setThreathenedAttributes(threats); //provides value of attribute and generality information

            //setContextChangeOccurances(threat); // provides value for frequency of change

            adaptationRule = getAdaptationRule();
            metaAdaptationRule = getAdaptationMetaRule();
            threatFrequencys = new List<double>();
            h_adaptationActionss = new List<List<string>>();

            decisions = new List<string>();
            maxUtilityFunctions = new List<double>();
            sensitivity_level_utilitys = new List<double>();
            obf_level_utilitys = new List<double>();

            threathenedOperationalAttributes = new List<Attribute>();

            foreach (Threat threat in threats)
            {
                Attribute att = getThreathenedAttribute(threat);
                bool contained = false;
                foreach (Attribute attx in threathenedOperationalAttributes)
                {
                    if (attx.name.Equals(att.name))
                    {
                        contained = true;
                        break;
                    }
                }

                if (contained == true)
                {
                    continue;
                }

                threathenedOperationalAttributes.Add(att);
                
                double attGen = 0;
                //NB that utility has been swapped for inverse of threat severity
                if (att.obfuscationLevel == Attribute.Obfuscation.InccurateImprecise)
                {
                    //attGen = attGen + 1;
                    attGen = attGen + 0.4;
                }
                else if (att.obfuscationLevel == Attribute.Obfuscation.AccurateImprecise)
                {
                    //attGen = attGen + 0.8;
                    attGen = attGen + 0.6;
                }
                else if (att.obfuscationLevel == Attribute.Obfuscation.InaccuratePrecise)
                {
                    //attGen = attGen + 0.6;
                    attGen = attGen + 0.8;
                }
                else if (att.obfuscationLevel == Attribute.Obfuscation.AccuratePrecise)
                {
                    //attGen = attGen + 0.4;
                    attGen = attGen + 1;
                }

                //attGen = attGen / threathenedOperationalAttributes.Count;
                double uThreatServerityGen = 0;
                for (int i = 1; i < AdaptationMgtConfigWindow.x_attGen.Count(); i++)
                {
                    if (AdaptationMgtConfigWindow.x_attGen[i - 1] <= attGen &&
                        AdaptationMgtConfigWindow.x_attGen[i] >= attGen)
                    {
                        double y_tf_i_minus_1 = AdaptationMgtConfigWindow.y_attGen[i - 1];
                        double y_tf_i = AdaptationMgtConfigWindow.y_attGen[i];
                        uThreatServerityGen = y_tf_i_minus_1 + (y_tf_i - y_tf_i_minus_1) *
                            ((attGen - AdaptationMgtConfigWindow.x_attGen[i - 1]) / (AdaptationMgtConfigWindow.x_attGen[i] - AdaptationMgtConfigWindow.x_attGen[i - 1])); //interpolate

                        uThreatServerityGen = Math.Round(uThreatServerityGen, 2);
                        obf_level_utilitys.Add(uThreatServerityGen);

                        break;
                    }
                }

                double attValue = 0;
                //NB that utility has been swapped for inverse of threat severity
                if (att.sensitivityLevel == Attribute.Sensitivity.veryHigh)
                {
                    //attValue = attValue + 1;
                    attValue = attValue + 0.2;
                }
                else if (att.sensitivityLevel == Attribute.Sensitivity.high)
                {
                    //attValue = attValue + 0.8;
                    attValue = attValue + 0.4;
                }
                else if (att.sensitivityLevel == Attribute.Sensitivity.medium)
                {
                    //attValue = attValue + 0.6;
                    attValue = attValue + 0.6;
                }
                else if (att.sensitivityLevel == Attribute.Sensitivity.low)
                {
                    //attValue = attValue + 0.4;
                    attValue = attValue + 0.8;
                }
                if (att.sensitivityLevel == Attribute.Sensitivity.veryLow)
                {
                    //attValue = attValue + 0.2;
                    attValue = attValue + 1;
                }

                //attValue = attValue / threathenedOperationalAttributes.Count;
                double uThreatServerityValue = 0;
                for (int i = 1; i < AdaptationMgtConfigWindow.x_AttVal.Count(); i++)
                {
                    if (AdaptationMgtConfigWindow.x_AttVal[i - 1] <= attValue &&
                        AdaptationMgtConfigWindow.x_AttVal[i] >= attValue)
                    {
                        double y_tf_i_minus_1 = AdaptationMgtConfigWindow.y_AttVal[i - 1];
                        double y_tf_i = AdaptationMgtConfigWindow.y_AttVal[i];
                        uThreatServerityValue = y_tf_i_minus_1 + (y_tf_i - y_tf_i_minus_1) *
                            ((attValue - AdaptationMgtConfigWindow.x_AttVal[i - 1]) / (AdaptationMgtConfigWindow.x_AttVal[i] - AdaptationMgtConfigWindow.x_AttVal[i - 1])); //interpolate

                        uThreatServerityValue = Math.Round(uThreatServerityValue, 2);
                        sensitivity_level_utilitys.Add(uThreatServerityValue);

                        break;
                    }
                }

                sensitivity_level_constant = AdaptationMgtConfigWindow.kv;
                obf_level_constant = AdaptationMgtConfigWindow.kg;

                //double expectedUtility = (AdaptationMgtConfigWindow.kcDelta * uThreatServerityCCF) + (AdaptationMgtConfigWindow.kv * uThreatServerityValue)
                //                            + (AdaptationMgtConfigWindow.kt * uThreatServerityTF) + (AdaptationMgtConfigWindow.kg * uThreatServerityGen);

                double expectedUtility = (AdaptationMgtConfigWindow.kv * uThreatServerityValue) + (AdaptationMgtConfigWindow.kg * uThreatServerityGen);
                expectedUtility = Math.Round(expectedUtility, 2);

                double maxUtilValue = 0;
                foreach (double utilx in AdaptationMgtConfigWindow.y_attGen)
                {
                    if (utilx > maxUtilValue)
                    {
                        maxUtilValue = utilx;
                    }
                }
                foreach (double utilx in AdaptationMgtConfigWindow.y_AttVal)
                {
                    if (utilx > maxUtilValue)
                    {
                        maxUtilValue = utilx;
                    }
                }

                double expectedUtilityNormalised = (expectedUtility / maxUtilValue);
                expectedUtilityNormalised = Math.Round(expectedUtilityNormalised, 2);

                maxUtilityFunctions.Add(expectedUtilityNormalised);

                string indAdaptationAction = "";

                if (threat.threatType == Threat.ThreatType.futureThreat)
                {
                    indAdaptationAction = "<potentialThreat>";
                }
                //NB that utility has been swapped for inverse of threat severity
                else if (expectedUtilityNormalised < AdaptationMgtConfigWindow.ignoreThreshExpectedUtility)
                {
                    //indAdaptationAction = "IGNORE";
                    indAdaptationAction = "TERMINATE";
                }
                else if (expectedUtilityNormalised >= AdaptationMgtConfigWindow.ignoreThreshExpectedUtility
                    && expectedUtilityNormalised < AdaptationMgtConfigWindow.reactThreshExpectedUtility)
                {
                    //indAdaptationAction = "REACT";
                    indAdaptationAction = "PREVENT";
                }
                else if (expectedUtilityNormalised >= AdaptationMgtConfigWindow.reactThreshExpectedUtility
                    && expectedUtilityNormalised < AdaptationMgtConfigWindow.preventThreshExpectedUtility)
                {
                    //indAdaptationAction = "PREVENT";
                    indAdaptationAction = "REACT";
                }                
                else if (expectedUtilityNormalised >= AdaptationMgtConfigWindow.preventThreshExpectedUtility)
                {
                    //indAdaptationAction = "TERMINATE";
                    indAdaptationAction = "IGNORE";
                }

                decisions.Add(indAdaptationAction);
            }
                        
            if (decisions.Contains("TERMINATE"))
            {
                adaptationAction = "TERMINATE";
            }
            else if (decisions.Contains("PREVENT"))
            {
                adaptationAction = "PREVENT";
            }
            else if (decisions.Contains("REACT"))
            {
                adaptationAction = "REACT";
            }
            else if (decisions.Contains("IGNORE"))
            {
                adaptationAction = "IGNORE";
            }
            else if (decisions.Contains("<potentialThreat>"))
            {
                adaptationAction = "<potential>";
            }

            //check meta adaptation rule
            //int threatFrequency = 1;
            List<string> h_adaptationActions = new List<string>();
            h_adaptationActions.Add(adaptationAction);

            foreach (Dictionary<List<Threat>, string> threatMitigationAction in threatMitigationActions)
            {
                foreach (KeyValuePair<List<Threat>, string> kvp in threatMitigationAction)
                {
                    List<Threat> h_threats = kvp.Key;
                    Threat h_threat = h_threats.ElementAt(0); //the assumption is that the states transitions and agents for all the threats are the same
                    Threat threat = threats.ElementAt(0);  //the assumption is that the states transitions and agents for all the threats are the same

                    string h_adaptationAction = kvp.Value;
                    bool sameState = false;
                    bool sameTransition = false;
                    bool sameAgents = false;
                    if (threat.message_s != null && h_threat.message_s != null)
                    {
                        if (threat.message_s.name.Equals(h_threat.message_s.name))
                        {
                            sameState = true;
                        }
                    }
                    else if (threat.message_t != null && h_threat.message_t != null)
                    {
                        if (threat.message_t.transitionName.Equals(h_threat.message_t.transitionName))
                        {
                            sameTransition = true;
                        }
                    }

                    if (threat.sendAgent.agentName.Equals(h_threat.sendAgent.agentName) &&
                        threat.subjectAgent.agentName.Equals(h_threat.subjectAgent.agentName) &&
                            threat.recieveAgent.agentName.Equals(h_threat.recieveAgent.agentName))
                    {
                        sameAgents = true;
                    }

                    if (sameAgents && (sameState || sameTransition))
                    {
                        //threatFrequency = threatFrequency + 1;
                        h_adaptationActions.Add(h_adaptationAction);
                    }
                }
            }
            h_adaptationActionss.Add(h_adaptationActions);
            //threatFrequencys.Add(h_adaptationActions.Count);

            if (h_adaptationActions.Contains("<potential>"))
            {
                adaptationAction = "<potential>";
                double noPotential = 0;
                foreach (string s in h_adaptationActions)
                {
                    if (s.Equals("<potential>"))
                    {
                        noPotential = noPotential + 1;
                    }
                }

                threatFrequencys.Add(noPotential);
            }
            else if (h_adaptationActions.Contains("TERMINATE"))
            {
                adaptationAction = "TERMINATE";
                double noTerminates = 0;
                foreach (string s in h_adaptationActions)
                {
                    if (s.Equals("TERMINATE"))
                    {
                        noTerminates = noTerminates + 1;
                    }
                }
                if (noTerminates > AdaptationMgtConfigWindow.metaTerminateThreshExpectedUtility)
                {
                    adaptationAction = adaptationAction + " → TERMINATE";
                }

                threatFrequencys.Add(noTerminates);
            }
            else if (h_adaptationActions.Contains("PREVENT"))
            {
                double noPrevents = 0;
                foreach (string s in h_adaptationActions)
                {
                    if (s.Equals("PREVENT"))
                    {
                        noPrevents = noPrevents + 1;
                    }
                }
                if (noPrevents > AdaptationMgtConfigWindow.metaTerminateThreshExpectedUtility)
                {
                    adaptationAction = adaptationAction + " → TERMINATE";
                }
                threatFrequencys.Add(noPrevents);

            }

            else if (h_adaptationActions.Contains("REACT"))
            {
                double noReacts = 0;
                foreach (string s in h_adaptationActions)
                {
                    if (s.Equals("REACT"))
                    {
                        noReacts = noReacts + 1;
                    }
                }
                if (noReacts > AdaptationMgtConfigWindow.metaPreventThreshExpectedUtility)
                {
                    adaptationAction = adaptationAction + " → PREVENT";
                }
                threatFrequencys.Add(noReacts);

            }

            else if (h_adaptationActions.Contains("IGNORE"))
            {
                double noIgnores = 0;
                foreach (string s in h_adaptationActions)
                {
                    if (s.Equals("IGNORE"))
                    {
                        noIgnores = noIgnores + 1;
                    }
                }
                if (noIgnores > AdaptationMgtConfigWindow.metaReactThreshExpectedUtility)
                {
                    adaptationAction = adaptationAction + " → REACT";
                }
                threatFrequencys.Add(noIgnores);
            }
            Dictionary<List<Threat>, string> threatMitigationAction_ = new Dictionary<List<Threat>, string>();
            threatMitigationAction_.Add(threats, adaptationAction);

            threatMitigationActions.Add(threatMitigationAction_);

            return adaptationAction;            
        }

        public static string getAdaptationRule()
        {
            //NB that utility has been swapped for inverse of threat severity
            string adaptationRule = "IF U(VaGa) ≤" + AdaptationMgtConfigWindow.ignoreThreshExpectedUtility + " Then TERMINATE" + "\r\n" +
                "IF " + AdaptationMgtConfigWindow.ignoreThreshExpectedUtility + "< U(VaGa) ≤" + AdaptationMgtConfigWindow.reactThreshExpectedUtility + " Then PREVENT" + "\r\n" +
                "IF " + AdaptationMgtConfigWindow.reactThreshExpectedUtility + "< U(VaGa) ≤" + AdaptationMgtConfigWindow.preventThreshExpectedUtility + " Then REACT" + "\r\n" +
                "IF U(VaGa) >" + AdaptationMgtConfigWindow.preventThreshExpectedUtility + " Then IGNORE";

            return adaptationRule;
        }

        public static string getAdaptationMetaRule()
        {
            string adaptationMetaRule = "IF IGNORE and Tf >" + AdaptationMgtConfigWindow.metaReactThreshExpectedUtility + " Then REACT" + "\r\n" +
                    "IF REACT and Tf >" + AdaptationMgtConfigWindow.metaPreventThreshExpectedUtility + " Then PREVENT" + "\r\n" +
                    "IF PREVENT and Tf >" + AdaptationMgtConfigWindow.metaTerminateThreshExpectedUtility + " Then TERMINATE";

            return adaptationMetaRule;
        }

        private static Attribute getThreathenedAttribute(Threat threat)
        {
            //Attribute attribute = null;
            PolicyStatementAnalyser psa = new PolicyStatementAnalyser();
            psa.ifThenUnlessTokenisation(threat.violatedPolicy.statement);

            Attribute ifAttribute = psa.getIfAttribute();

            //List<Attribute> p_attributes = null;
            //if (threat.message_s != null)
            //{
            //    p_attributes = threat.message_s.activeAction.parameterisedOperationalAttributes;

            //}
            //else if (threat.message_t != null)
            //{
            //    p_attributes = threat.message_t.activeEvent.parameterisedOperationalAttributes;
            //}

            //if (p_attributes != null)
            //{
            //    foreach (Attribute att in p_attributes)
            //    {
            //        if (att.name.Equals(ifAttribute.name))
            //        {
            //            attribute = att;
            //            break;
            //        }
            //    }
            //}

            return ifAttribute;
        }


        private static void setThreathenedAttributes(List<Threat> threats)
        {
            threathenedOperationalAttributes = new List<Attribute>();

            // get operational attributes in policy/ if aggregated threat then get policys from aggregated attributes
            foreach (Threat threat in threats)
            {
                PolicyStatementAnalyser psa = new PolicyStatementAnalyser();
                psa.ifThenUnlessTokenisation(threat.violatedPolicy.statement);

                Attribute ifAttribute = psa.getIfAttribute();

                List<Attribute> p_attributes = null;
                if (threat.message_s != null)
                {
                    p_attributes = threat.message_s.activeAction.parameterisedOperationalAttributes;
                    
                }
                else if (threat.message_t != null)
                {
                    p_attributes = threat.message_t.activeEvent.parameterisedOperationalAttributes;
                }

                if (p_attributes != null)
                {
                    foreach (Attribute attribute in p_attributes)
                    {
                        if (attribute.name.Equals(ifAttribute.name))
                        {
                            threathenedOperationalAttributes.Add(attribute);
                        }
                    }
                }                
            }            
        }

        //private static void setNoThreatOccurances(Threat threat)
        //{
        //    noDefinedThreatOccurance = 1;
        //    totalNoThreatOccurance = 1;
        //    foreach (Threat threatx in EmulationEnvironmentWindow.misuseAnalyser.threats)
        //    {
        //        totalNoThreatOccurance = totalNoThreatOccurance+1;

        //        if (threat.sendAgent.agentName.Equals(threatx.sendAgent.agentName)
        //            && threat.recieveAgent.agentName.Equals(threatx.recieveAgent.agentName)
        //            && threat.subjectAgent.agentName.Equals(threatx.subjectAgent.agentName))
        //        {
        //            if (threat.message_s != null)
        //            {
        //                List<Attribute> operationalAttributes = threat.message_s.activeAction.parameterisedOperationalAttributes;
        //                bool contained = false;
        //                foreach (Attribute operationalAtt in operationalAttributes)
        //                {
        //                    if (threatx.message_s != null)
        //                    {
        //                        List<Attribute> operationalAttributesx = threatx.message_s.activeAction.parameterisedOperationalAttributes;
        //                        foreach (Attribute operationalAttx in operationalAttributesx)
        //                        {
        //                            if (operationalAttx.name.Equals(operationalAtt.name))
        //                            {
        //                                contained = true;
        //                                break;
        //                            }
        //                        }
        //                    }
        //                    else if (threatx.message_t != null)
        //                    {
        //                        List<Attribute> operationalAttributesx = threatx.message_t.activeEvent.parameterisedOperationalAttributes;
        //                        foreach (Attribute operationalAttx in operationalAttributesx)
        //                        {
        //                            if (operationalAttx.name.Equals(operationalAtt.name))
        //                            {
        //                                contained = true;
        //                                break;
        //                            }
        //                        }
        //                    }
        //                }
        //                if(contained == false)
        //                {
        //                    continue;
        //                }

        //            }
        //            else if (threat.message_t != null)
        //            {
        //                List<Attribute> operationalAttributes = threat.message_t.activeEvent.parameterisedOperationalAttributes;
        //                bool contained = false;
        //                foreach (Attribute operationalAtt in operationalAttributes)
        //                {
        //                    if (threatx.message_s != null)
        //                    {
        //                        List<Attribute> operationalAttributesx = threatx.message_s.activeAction.parameterisedOperationalAttributes;
        //                        foreach (Attribute operationalAttx in operationalAttributesx)
        //                        {
        //                            if (operationalAttx.name.Equals(operationalAtt.name))
        //                            {
        //                                contained = true;
        //                                break;
        //                            }
        //                        }
        //                    }
        //                    else if (threatx.message_t != null)
        //                    {
        //                        List<Attribute> operationalAttributesx = threatx.message_t.activeEvent.parameterisedOperationalAttributes;
        //                        foreach (Attribute operationalAttx in operationalAttributesx)
        //                        {
        //                            if (operationalAttx.name.Equals(operationalAtt.name))
        //                            {
        //                                contained = true;
        //                                break;
        //                            }
        //                        }
        //                    }
        //                }
        //                if (contained == false)
        //                {
        //                    continue;
        //                }

        //            }

        //            noDefinedThreatOccurance = noDefinedThreatOccurance + 1;
        //        }
        //    }

        //    foreach (Threat threatx in EmulationEnvironmentWindow.aggregationMisuseAnalyser.aggregationThreats)
        //    {
        //        totalNoThreatOccurance = totalNoThreatOccurance+1;

        //        if (threat.sendAgent.agentName.Equals(threatx.sendAgent.agentName)
        //            && threat.recieveAgent.agentName.Equals(threatx.recieveAgent.agentName)
        //            && threat.subjectAgent.agentName.Equals(threatx.subjectAgent.agentName))
        //        {
        //            if (threat.message_s != null)
        //            {
        //                List<Attribute> operationalAttributes = threat.message_s.activeAction.parameterisedOperationalAttributes;
        //                bool contained = false;
        //                foreach (Attribute operationalAtt in operationalAttributes)
        //                {
        //                    if (threatx.message_s != null)
        //                    {
        //                        List<Attribute> operationalAttributesx = threat.message_s.activeAction.parameterisedOperationalAttributes;
        //                        foreach (Attribute operationalAttx in operationalAttributesx)
        //                        {
        //                            if (operationalAttx.name.Equals(operationalAtt.name))
        //                            {
        //                                contained = true;
        //                            }
        //                        }
        //                    }
        //                    else if (threatx.message_t != null)
        //                    {
        //                        List<Attribute> operationalAttributesx = threat.message_t.activeEvent.parameterisedOperationalAttributes;
        //                        foreach (Attribute operationalAttx in operationalAttributesx)
        //                        {
        //                            if (operationalAttx.name.Equals(operationalAtt.name))
        //                            {
        //                                contained = true;
        //                            }
        //                        }
        //                    }
        //                }
        //                if(contained == false)
        //                {
        //                    continue;
        //                }

        //            }
        //            else if (threat.message_t != null)
        //            {
        //                List<Attribute> operationalAttributes = threat.message_t.activeEvent.parameterisedOperationalAttributes;
        //                bool contained = false;
        //                foreach (Attribute operationalAtt in operationalAttributes)
        //                {
        //                    if (threatx.message_s != null)
        //                    {
        //                        List<Attribute> operationalAttributesx = threat.message_s.activeAction.parameterisedOperationalAttributes;
        //                        foreach (Attribute operationalAttx in operationalAttributesx)
        //                        {
        //                            if (operationalAttx.name.Equals(operationalAtt.name))
        //                            {
        //                                contained = true;
        //                            }
        //                        }
        //                    }
        //                    else if (threatx.message_t != null)
        //                    {
        //                        List<Attribute> operationalAttributesx = threat.message_t.activeEvent.parameterisedOperationalAttributes;
        //                        foreach (Attribute operationalAttx in operationalAttributesx)
        //                        {
        //                            if (operationalAttx.name.Equals(operationalAtt.name))
        //                            {
        //                                contained = true;
        //                            }
        //                        }
        //                    }
        //                }
        //                if (contained == false)
        //                {
        //                    continue;
        //                }

        //            }

        //            noDefinedThreatOccurance = noDefinedThreatOccurance + 1;
        //        }
        //    }
        //}
    }
}
